package org.langcom.crypt;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.X509Certificate;

public class SignedObject<S extends Serializable> implements Serializable {

	private static final long serialVersionUID = 3545793295298345011L;
	private DigitalSignature signature;
	private byte[] content;
	private X509Certificate certificate;

	public SignedObject(S object, X509Certificate certificate, ObjectSigner signer) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, IOException {
		ByteArrayOutputStream bytes = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(bytes);
		out.writeObject(object);
		content = bytes.toByteArray();
		this.certificate = certificate;
		signature = signer.sign(object);
		if (!signature.verify(certificate.getPublicKey(), object)) {
			throw new IllegalArgumentException("Public key should correspond to signer's private key");
		}
	}

	@SuppressWarnings("unchecked")
	public S getObject() {
		try {
			ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(content));
			return (S) in.readObject();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public byte[] getContent() {
		return content;
	}

	public byte[] getRootContent() {
		byte[] rootContent = null;
		Object obj = null;
		try {
			rootContent = getContent();
			ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(content));
			obj = in.readObject();
		} catch (Exception e) {
		}
		if (obj instanceof SignedObject) {
			return ((SignedObject<?>) obj).getRootContent();
		}
		return rootContent;
	}

	public DigitalSignature getSignature() {
		return signature;
	}

	public X509Certificate getCertificate() {
		return certificate;
	}

	public boolean verify() throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
		return signature.verify(certificate.getPublicKey(), content);
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof SignedObject)) {
			return false;
		}
		SignedObject<?> so = (SignedObject<?>) obj;
		return signature.equals(so.signature) && certificate.equals(so.certificate);
	}

	public Object getRootObject() {
		Object obj = getObject();
		if (obj instanceof SignedObject) {
			return ((SignedObject<?>) obj).getRootObject();
		}
		return obj;
	}
}
